/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package spynx.desl.impl;

import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;

import spynx.desl.Argument;
import spynx.desl.DeslPackage;
import spynx.desl.Expression;
import spynx.desl.LocateStatement;
import spynx.desl.Proxy;
import spynx.desl.runtime.IExecutionContext;
import spynx.desl.runtime.IPatternMatchInfo;
import spynx.desl.runtime.ITextLocator;
import spynx.util.PatternMismatchedException;

/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>Locate Statement</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link spynx.desl.impl.LocateStatementImpl#getPosition <em>Position</em>}</li>
 *   <li>{@link spynx.desl.impl.LocateStatementImpl#isOptional <em>Optional</em>}</li>
 *   <li>{@link spynx.desl.impl.LocateStatementImpl#getLocator <em>Locator</em>}</li>
 *   <li>{@link spynx.desl.impl.LocateStatementImpl#getFlag <em>Flag</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class LocateStatementImpl extends StatementImpl implements LocateStatement {
	/**
	 * The default value of the '{@link #getPosition() <em>Position</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getPosition()
	 * @generated
	 * @ordered
	 */
	protected static final String POSITION_EDEFAULT = "begin";

	/**
	 * The cached value of the '{@link #getPosition() <em>Position</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getPosition()
	 * @generated
	 * @ordered
	 */
	protected String position = POSITION_EDEFAULT;

	/**
	 * The default value of the '{@link #isOptional() <em>Optional</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #isOptional()
	 * @generated
	 * @ordered
	 */
	protected static final boolean OPTIONAL_EDEFAULT = false;

	/**
	 * The cached value of the '{@link #isOptional() <em>Optional</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #isOptional()
	 * @generated
	 * @ordered
	 */
	protected boolean optional = OPTIONAL_EDEFAULT;

	/**
	 * The cached value of the '{@link #getLocator() <em>Locator</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getLocator()
	 * @generated
	 * @ordered
	 */
	protected Proxy locator;

	/**
	 * The cached value of the '{@link #getFlag() <em>Flag</em>}' containment reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getFlag()
	 * @generated
	 * @ordered
	 */
	protected Expression flag;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	protected LocateStatementImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return DeslPackage.Literals.LOCATE_STATEMENT;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public String getPosition() {
		return position;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setPosition(String newPosition) {
		String oldPosition = position;
		position = newPosition;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOCATE_STATEMENT__POSITION, oldPosition, position));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public boolean isOptional() {
		return optional;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setOptional(boolean newOptional) {
		boolean oldOptional = optional;
		optional = newOptional;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOCATE_STATEMENT__OPTIONAL, oldOptional, optional));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Proxy getLocator() {
		if (locator != null && locator.eIsProxy()) {
			InternalEObject oldLocator = (InternalEObject)locator;
			locator = (Proxy)eResolveProxy(oldLocator);
			if (locator != oldLocator) {
				if (eNotificationRequired())
					eNotify(new ENotificationImpl(this, Notification.RESOLVE, DeslPackage.LOCATE_STATEMENT__LOCATOR, oldLocator, locator));
			}
		}
		return locator;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Proxy basicGetLocator() {
		return locator;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setLocator(Proxy newLocator) {
		Proxy oldLocator = locator;
		locator = newLocator;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOCATE_STATEMENT__LOCATOR, oldLocator, locator));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Expression getFlag() {
		return flag;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetFlag(Expression newFlag, NotificationChain msgs) {
		Expression oldFlag = flag;
		flag = newFlag;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, DeslPackage.LOCATE_STATEMENT__FLAG, oldFlag, newFlag);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setFlag(Expression newFlag) {
		if (newFlag != flag) {
			NotificationChain msgs = null;
			if (flag != null)
				msgs = ((InternalEObject)flag).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOCATE_STATEMENT__FLAG, null, msgs);
			if (newFlag != null)
				msgs = ((InternalEObject)newFlag).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOCATE_STATEMENT__FLAG, null, msgs);
			msgs = basicSetFlag(newFlag, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOCATE_STATEMENT__FLAG, newFlag, newFlag));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
		switch (featureID) {
			case DeslPackage.LOCATE_STATEMENT__FLAG:
				return basicSetFlag(null, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case DeslPackage.LOCATE_STATEMENT__POSITION:
				return getPosition();
			case DeslPackage.LOCATE_STATEMENT__OPTIONAL:
				return isOptional();
			case DeslPackage.LOCATE_STATEMENT__LOCATOR:
				if (resolve) return getLocator();
				return basicGetLocator();
			case DeslPackage.LOCATE_STATEMENT__FLAG:
				return getFlag();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case DeslPackage.LOCATE_STATEMENT__POSITION:
				setPosition((String)newValue);
				return;
			case DeslPackage.LOCATE_STATEMENT__OPTIONAL:
				setOptional((Boolean)newValue);
				return;
			case DeslPackage.LOCATE_STATEMENT__LOCATOR:
				setLocator((Proxy)newValue);
				return;
			case DeslPackage.LOCATE_STATEMENT__FLAG:
				setFlag((Expression)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case DeslPackage.LOCATE_STATEMENT__POSITION:
				setPosition(POSITION_EDEFAULT);
				return;
			case DeslPackage.LOCATE_STATEMENT__OPTIONAL:
				setOptional(OPTIONAL_EDEFAULT);
				return;
			case DeslPackage.LOCATE_STATEMENT__LOCATOR:
				setLocator((Proxy)null);
				return;
			case DeslPackage.LOCATE_STATEMENT__FLAG:
				setFlag((Expression)null);
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case DeslPackage.LOCATE_STATEMENT__POSITION:
				return POSITION_EDEFAULT == null ? position != null : !POSITION_EDEFAULT.equals(position);
			case DeslPackage.LOCATE_STATEMENT__OPTIONAL:
				return optional != OPTIONAL_EDEFAULT;
			case DeslPackage.LOCATE_STATEMENT__LOCATOR:
				return locator != null;
			case DeslPackage.LOCATE_STATEMENT__FLAG:
				return flag != null;
		}
		return super.eIsSet(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		if (eIsProxy()) return super.toString();

		StringBuffer result = new StringBuffer(super.toString());
		result.append(" (position: ");
		result.append(position);
		result.append(", optional: ");
		result.append(optional);
		result.append(')');
		return result.toString();
	}

	@Override
	public Object execute(IExecutionContext context) {
		IPatternMatchInfo matchInfo = (IPatternMatchInfo) context.Variables().getVariable("@this");
		List<Argument> args = getChildren(Argument.class);
		int flag = this.getFlag() != null ? 
					(Integer) this.getFlag().execute(context): ITextLocator.OPT_DEFAULT;
		String position = this.getPosition();
		ITextLocator locator = getLocator().unwrap(context);
		locator.setArguments(args);
		
		int loc = locator.locate(context, matchInfo.getPosition(), matchInfo.getLocalInput());
		
		if (loc >= 0) {
			loc += matchInfo.getBeginLocation(); // turn loc in to global position.
			if ("begin".equals(position)) {
				matchInfo.setBeginLocation((flag & ITextLocator.OPT_EXCLUSIVE) > 0 ? loc + locator.getLength() : loc);
				// set the local position to the lenght of match text
				matchInfo.setPosition(locator.getLength(), IPatternMatchInfo.SEEK_LOCAL_FROM_BEGIN);
			}
			if ("end".equals(position))
				matchInfo.setEndLocation( (flag & ITextLocator.OPT_EXCLUSIVE) > 0 ? loc : loc + locator.getLength());
			
			//matchInfo.setPosition(loc + locator.getLength() , IPatternMatchInfo.SEEK_FROM_BEGIN);
		}
		else if (!isOptional()) {
			matchInfo.setMatched(false);
			throw new PatternMismatchedException(String.format("Unable to locate '%s' in the input", locator.getValue()));
		}
		
		matchInfo.set(position, locator);
		
		return locator;
	}
} //LocateStatementImpl
